﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using BootyShaker.Utils;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using FarseerPhysics.Factories;
using FarseerPhysics.SamplesFramework;
//using Microsoft.Phone.Controls;
using Microsoft.Xna.Framework.Graphics;
using System.Windows.Input;


namespace BootyShaker
{

    public partial class ShakeObjectCtl : UserControl
    {

        // public System.Windows.Point destination = new Point(0, 0);
        public System.Windows.Point DesignLocation
        {
            get
            {
                // return new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
                return RenderTransform.Transform(new Point(0, 0));

            }
            set
            {
                objTransform.TranslateX = value.X;
                objTransform.TranslateY = value.Y;
                //Canvas.SetLeft(this, value.X);
                //Canvas.SetTop(this, value.Y);
            }
        }
        public Size size
        {
            get
            {
                // return new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
                return new Size(this.ActualWidth * ((CompositeTransform)this.RenderTransform).ScaleX, this.ActualHeight * ((CompositeTransform)this.RenderTransform).ScaleY);
            }
            //set
            //{
            //    objTransform.TranslateX = value.X;
            //    objTransform.TranslateY = value.Y;
            //    //Canvas.SetLeft(this, value.X);
            //    //Canvas.SetTop(this, value.Y);
            //}
        }
        public Size getsize()
        {
            return new Size(this.ActualWidth * ((CompositeTransform)this.RenderTransform).ScaleX, this.ActualHeight * ((CompositeTransform)this.RenderTransform).ScaleY);
        }
        public System.Windows.Point Location
        {
            get;
            set;
        }
        private Canvas parent;

        FixedDistanceJoint joint;
        public Body _circle;
        private Image shakeImage;
        public List<Texture2D> texturesList;
        public ShakeObjectCtl(System.Windows.Point loc, Size sz, Canvas ctl, Image img, Action deleteCallback)
        {
            InitializeComponent();
            parent = ctl;
            DeleteCallback = deleteCallback;
            loc = Application.Current.RootVisual.TransformToVisual(ctl).Transform(loc);
            shakeImage = img;
            this.DesignLocation = loc;
            //this.size = sz;
            this.Width = sz.Width;
            this.Height = sz.Height;

            Canvas.SetZIndex(this, Int16.MaxValue - 1);


            this.RenderTransform = objTransform;
            CloseBtnX.RenderTransform = CloseButtonTransform;
            CloseBtnCirc.RenderTransform = CloseButtonTransform;
            this.MaxWidth = Application.Current.Host.Content.ActualWidth;
            this.MaxHeight = Application.Current.Host.Content.ActualWidth;
            mainellipse.Stroke = new SolidColorBrush(Color.FromArgb(0xB5, 0xff, 0xff, 0xff));
            //foreach (ShakeObjectCtl obj in App.shakeObjects)
            //{
            //    mainellipse.Clip = obj.mainellipse
            //}
            MergeOverlay();
        }
        private CompositeTransform objTransformInvTmp = new CompositeTransform();
        //private TransformGroup objTransformInvGrp = new TransformGroup();
        //private MatrixTransform objTransformInv = new MatrixTransform();



        public void MergeOverlay()
        {
            if (new AppSettings().MergeOverlaySetting)
            {
                imageEllipse.Visibility = System.Windows.Visibility.Visible;
                ImageBrush ib = new ImageBrush();
                TransformGroup tg = new TransformGroup();
                tg.Children.Add(shakeImage.RenderTransform);
                calculateInverseTransform();
                tg.Children.Add(objTransformInvTmp);


                ib.ImageSource = shakeImage.Source;
                ib.Stretch = Stretch.None;
                ib.AlignmentX = AlignmentX.Left;
                ib.AlignmentY = AlignmentY.Top;

                ib.Transform = tg;
                imageEllipse.Fill = ib;

                imageEllipse.Opacity = 0.9;
                imageEllipse.StrokeThickness = 1;
                imageEllipse.Stroke = new SolidColorBrush(Color.FromArgb(0xB5, 0xff, 0xff, 0xff));
            }
            else
            {
                imageEllipse.Fill = null;
                imageEllipse.Visibility = System.Windows.Visibility.Collapsed;
            }

        }
        Matrix Multiply(Matrix A, Matrix B)
        {
            return new Matrix(A.M11 * B.M11 + A.M12 * B.M21,
                              A.M11 * B.M12 + A.M12 * B.M22,
                              A.M21 * B.M11 + A.M22 * B.M21,
                              A.M21 * B.M12 + A.M22 * B.M22,
                              A.OffsetX * B.M11 + A.OffsetY * B.M21 + B.OffsetX,
                              A.OffsetX * B.M12 + A.OffsetY * B.M22 + B.OffsetY);
        }
        private CompositeTransform objTransform = new CompositeTransform();
        //private double initialAngle;

        private double initialAngle;
        private double initialScale;
        private Point initialTPLocation;
        private Point previousTPLocation;
        private Point initialTP1Location;
        private Point initialTP2Location;
        bool drag = false;
        bool scale = false;
        public bool Touch_FrameReported(object sender, System.Windows.Input.TouchFrameEventArgs e)
        {
            bool handled = false;
            try
            {
                System.Windows.Input.TouchPointCollection tpc = e.GetTouchPoints(Application.Current.RootVisual);

                if (tpc.Count == 1)
                {
                    TouchPoint tp = tpc[0];
                    bool intersects = Util.intersects(Util.ControlBounds(this), Application.Current.RootVisual.TransformToVisual(this).Transform(tp.Position));
                    if (tp.Action == TouchAction.Down && !drag && intersects)
                    {

                        initialTPLocation = tp.Position;
                        previousTPLocation = tp.Position;
                        drag = true;
                        handled = true;
                    }
                    else if (tp.Action == TouchAction.Move && drag)
                    {
                        //Canvas.SetLeft(shakeImage,Canvas.GetLeft(shakeImage) + tp.Position.X - initiallocation.X);
                        //Canvas.SetTop(shakeImage, Canvas.GetTop(shakeImage) + tp.Position.Y - initiallocation.Y);

                        objTransform.TranslateX += tp.Position.X - previousTPLocation.X;
                        objTransform.TranslateY += tp.Position.Y - previousTPLocation.Y;
                        previousTPLocation = tp.Position;
                        handled = true;
                    }
                    else if (tp.Action == TouchAction.Up && drag)
                    {
                        ApplyTransform();
                        bringToFront();
                        drag = false;
                        handled = true;
                       
                    }
                    else if (intersects) handled = true;
                    //asd();
                }
                else if (tpc.Count == 2)
                {
                    TouchPoint tp1 = tpc[0];
                    TouchPoint tp2 = tpc[1];
                    drag = false;
                    bool intersects = Util.intersects(Util.ControlBounds(this), Application.Current.RootVisual.TransformToVisual(this).Transform(tp1.Position)) && Util.intersects(Util.ControlBounds(this), Application.Current.RootVisual.TransformToVisual(this).Transform(tp2.Position));
                    if ((tp1.Action == TouchAction.Down || tp2.Action == TouchAction.Down) && intersects)
                    {
                        initialTP1Location = tp1.Position;
                        initialTP2Location = tp2.Position;
                        initialScale = objTransform.ScaleY;

                        //initialAngle = objTransform.Rotation;
                        Point center = Application.Current.RootVisual.TransformToVisual(this).Transform(Util.Center(initialTP2Location, initialTP1Location));
                        objTransform.CenterX = center.X;
                        objTransform.CenterY = center.Y;
                        scale = true;
                        handled = true;
                        CloseBtnX.Visibility = System.Windows.Visibility.Collapsed;
                        CloseBtnCirc.Visibility = System.Windows.Visibility.Collapsed;
                    }
                    else if ((tp1.Action == TouchAction.Move || tp2.Action == TouchAction.Move) && (tp1.Action != TouchAction.Up && tp2.Action != TouchAction.Up) && scale)
                    {

                        Util.Distance(tp1.Position, tp2.Position);
                        Util.Distance(initialTP1Location, initialTP2Location);
                        //Math.Max(Math.Min(this.Width * objTransform.ScaleX,this.MaxHeight),this.MinWidth)
                        double newscale = initialScale * Util.Distance(tp1.Position, tp2.Position) / Util.Distance(initialTP1Location, initialTP2Location);
                        if (this.Width * newscale < this.MaxWidth && this.Width * newscale > this.MinWidth && this.Height * newscale < this.MaxHeight && this.Height * newscale > this.MinHeight)
                        {
                            objTransform.ScaleX = objTransform.ScaleY = newscale;
                        }
                        //objTransform.Rotation = initialAngle + Util.angle(tp1.Position, tp2.Position) - Util.angle(initialTP1Location, initialTP2Location);
                        handled = true;
                    }
                    else if ((tp1.Action == TouchAction.Up || tp2.Action == TouchAction.Up) && scale)
                    {
                        ApplyTransform();
                        bringToFront();
                        scale = false;
                        handled = true;
                    }
                    else if (intersects) handled = true;
                }
            }
            catch { }
          
            calculateInverseTransform();

            return handled;
        }
        private void bringToFront() 
        {
            
            App.shakeObjects.Remove(this);
            App.shakeObjects.Add(this);
            int i=0;
            foreach(ShakeObjectCtl obj in App.shakeObjects)
            {
                Canvas.SetZIndex(obj, i++);
            }
         
        }
        private void calculateInverseTransform()
        {
            objTransformInvTmp.ScaleY = 1 / objTransform.ScaleY;
            objTransformInvTmp.ScaleX = 1 / objTransform.ScaleX;
            objTransformInvTmp.TranslateX = -objTransform.TranslateX / objTransform.ScaleX;
            objTransformInvTmp.TranslateY = -objTransform.TranslateY / objTransform.ScaleY;

            objTransformInvTmp.CenterY = objTransform.CenterY;
            objTransformInvTmp.CenterX = objTransform.CenterX;
        }
        //private void OnTap(object sender, GestureEventArgs e)
        //{
        //    objTransform.TranslateX = objTransform.TranslateY = 0;
        //    e.Handled = true;
        //}

        //private void OnDoubleTap(object sender, GestureEventArgs e)
        //{
        //    //objTransform.ScaleX = objTransform.ScaleY = 1;
        //    e.Handled = true;
        //}

        //private void OnHold(object sender, GestureEventArgs e)
        //{
        //    //objTransform.TranslateX = objTransform.TranslateY = 0;
        //    //objTransform.ScaleX = objTransform.ScaleY = 1;
        //    //objTransform.Rotation = 0;
        //    //e.Handled = true;
        //}

        //private void OnDragStarted(object sender, DragStartedGestureEventArgs e)
        //{
        //    // border.Background = greenBrush;
        //    e.Handled = true;
        //}

        //private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
        //{
        //    objTransform.TranslateX += e.HorizontalChange;
        //    objTransform.TranslateY += e.VerticalChange;
        //    e.Handled = true;
        //}

        //private void OnDragCompleted(object sender, DragCompletedGestureEventArgs e)
        //{
        //    // border.Background = normalBrush;
        //    Point tran = objTransform.Transform(new Point(0, 0));
        //    //Point loc = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
        //    //Canvas.SetLeft(this, loc.X + tran.X);
        //    DesignLocation = new Point(DesignLocation.X + tran.X, DesignLocation.Y + tran.Y);
        //    //Canvas.SetTop(this, loc.Y + tran.Y);
        //    objTransform.TranslateX = 0;
        //    objTransform.TranslateY = 0;
        //    e.Handled = true;
        //}

        //private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
        //{

        //    //border.Background = redBrush;
        //    //initialAngle = objTransform.Rotation;
        //    objTransform.CenterX = (e.GetPosition((UIElement)e.OriginalSource, 0).X + e.GetPosition((UIElement)e.OriginalSource, 1).X) / 2;
        //    objTransform.CenterY = (e.GetPosition((UIElement)e.OriginalSource, 0).Y + e.GetPosition((UIElement)e.OriginalSource, 1).Y) / 2;
        //    initialScale = objTransform.ScaleX;
        //    CloseBtnX.Visibility = System.Windows.Visibility.Collapsed;
        //    CloseBtnCirc.Visibility = System.Windows.Visibility.Collapsed;
        //    e.Handled = true;
        //}

        //private void OnPinchDelta(object sender, PinchGestureEventArgs e)
        //{

        //    if (!(objTransform.ScaleX * this.ActualWidth > this.MaxWidth ||
        //        objTransform.ScaleY * this.ActualHeight > this.MaxHeight ||
        //        objTransform.ScaleX * this.ActualWidth < this.MinWidth ||
        //        objTransform.ScaleY * this.ActualHeight < this.MinHeight))
        //    {
        //        objTransform.ScaleX = objTransform.ScaleY = initialScale * e.DistanceRatio;
        //    }

        //    //objTransform.Rotation = initialAngle + e.TotalAngleDelta;

        //    e.Handled = true;
        //}

        //private void OnPinchCompleted(object sender, PinchGestureEventArgs e)
        //{

        //    Point tran = objTransform.Transform(new Point(0, 0));
        //    //Point loc = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
        //    //Canvas.SetLeft(this, loc.X + tran.X);
        //    DesignLocation = new Point(DesignLocation.X + tran.X, DesignLocation.Y + tran.Y);
        //    //Canvas.SetTop(this, loc.Y + tran.Y);
        //    objTransform.TranslateX = 0;
        //    objTransform.TranslateY = 0;
        //    objTransform.CenterX = 0;
        //    objTransform.CenterY = 0;
        //    //border.Background = normalBrush;
        //    this.Width = this.Width * objTransform.ScaleX;
        //    objTransform.ScaleX = 1;
        //    this.Height = this.Height * objTransform.ScaleY;
        //    objTransform.ScaleY = 1;
        //    CloseBtnX.Visibility = System.Windows.Visibility.Visible;
        //    CloseBtnCirc.Visibility = System.Windows.Visibility.Visible;

        //    e.Handled = true;
        //}

        //private void OnFlick(object sender, FlickGestureEventArgs e)
        //{
        //    // flickData.Text = string.Format("{0} Flick: Angle {1} Velocity {2},{3}",
        //    //  e.Direction, Math.Round(e.Angle), e.HorizontalVelocity, e.VerticalVelocity);
        //    e.Handled = true;
        //}
        CompositeTransform CloseButtonTransform = new CompositeTransform();
        private void ApplyTransform()
        {

            //Point tran = objTransform.Transform(new Point(0, 0));
            ////Point loc = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));
            ////Canvas.SetLeft(this, loc.X + tran.X);
            //DesignLocation = new Point(DesignLocation.X + tran.X, DesignLocation.Y + tran.Y);
            ////Canvas.SetTop(this, loc.Y + tran.Y);
            //objTransform.TranslateX = 0;
            //objTransform.TranslateY = 0;
            //objTransform.CenterX = 0;
            //objTransform.CenterY = 0;
            ////border.Background = normalBrush;
            //this.Width = Math.Max(Math.Min(this.Width * objTransform.ScaleX, this.MaxHeight), this.MinWidth);
            //objTransform.ScaleX = 1;
            //this.Height = Math.Max(Math.Min(this.Height * objTransform.ScaleY, this.MaxHeight), this.MinHeight);
            //objTransform.ScaleY = 1;
            CloseButtonTransform.ScaleY = 1 / objTransform.ScaleY;
            CloseButtonTransform.ScaleX = 1 / objTransform.ScaleX;
            CloseBtnX.Visibility = System.Windows.Visibility.Visible;
            CloseBtnCirc.Visibility = System.Windows.Visibility.Visible;


        }

        private WriteableBitmap cropImage(WriteableBitmap img)
        {
            Size siz = getsize();
            WriteableBitmap CroppedImage = new WriteableBitmap((int)siz.Width, (int)siz.Height);

            // Copy the pixels from the targeted region of the source image into the target image, 
            // using the calculated offset
            for (int k = 0; k < CroppedImage.Pixels.Length; k++)
            {
                int x = (int)((k % CroppedImage.PixelWidth) + this.Location.X - siz.Width / 2 - App.ShakeImagePos.X);
                int y = (int)((k / CroppedImage.PixelWidth) + this.Location.Y - siz.Height / 2 - App.ShakeImagePos.Y);
                if (x > 0 && y > 0)
                {
                    try
                    {
                        CroppedImage.Pixels[k] = img.Pixels[y * img.PixelWidth + x];
                    }
                    catch
                    {
                    }
                }
            }
            return CroppedImage;
        }



        public Texture2D circle;
        //int StepFraction = 30;
        //int minStep = 4;
        public int TextureCount;
        public Texture2D LoadImage(GraphicsDevice gd, World World)
        {
            //TODO - gal viska su Float??
            double sizeSum = 0;
            foreach (ShakeObject obj in App.currentShaker.shakeObjects)
            {
                sizeSum += (obj.size.Width + obj.size.Height) / 2;
            }


            int StepFraction = (int)(((this.size.Width) / (sizeSum / App.currentShaker.shakeObjects.Count)) * (this.size.Width / 11));
            StepFraction = Math.Max(StepFraction, 10);
            StepFraction = Math.Min(StepFraction, 20);
            int start = StepFraction;
            int end = start / 3;

            Size siz = getsize();
            //Point loc = new Point(Canvas.GetLeft(this), Canvas.GetTop(this));

            Point tran = objTransform.Transform(new Point(0, 0));


            //this.Location = App.gt.Transform(this.RenderTransform.Transform(new Point(DesignLocation.X + tran.X + this.ActualWidth / 2, DesignLocation.Y + tran.Y + this.ActualHeight / 2)));
            this.Location = App.gt.Transform(new Point(DesignLocation.X + this.size.Width / 2, DesignLocation.Y + this.size.Height / 2));
            WriteableBitmap shakeObjImg = cropImage(App.ShakeImageTransformed);
            texturesList = new List<Texture2D>();

            for (int i = start; i > end; i--)
            {

                Size sz = new Size(siz.Width * i / StepFraction, siz.Height * i / StepFraction);
                Ellipse ellipse = new Ellipse();

                ImageBrush ib = new ImageBrush();

                ib.ImageSource = shakeObjImg;
                ib.Stretch = Stretch.None;
                ellipse.Fill = ib;
                RadialGradientBrush rgb = new RadialGradientBrush(Colors.Black, Colors.Transparent);
                rgb.GradientStops[0].Offset = 0.2;
                ellipse.OpacityMask = rgb;
                ellipse.StrokeThickness = 1;
                //ellipse.Stroke = new SolidColorBrush(Colors.Red);
                ellipse.Width = sz.Width;
                ellipse.Height = sz.Height;

                WriteableBitmap texture = new WriteableBitmap((int)ellipse.Width, (int)ellipse.Height);
                texture.Render(ellipse, new TranslateTransform());
                texture.Invalidate();
                Texture2D txt = new Texture2D(gd, texture.PixelWidth, texture.PixelHeight);
                txt.SetData(Imaging.BGRAToRGBA(texture));
                texturesList.Add(txt);
            }
            AppSettings settings = new AppSettings();

            float radius = (ConvertUnits.ToSimUnits(siz.Width) + ConvertUnits.ToSimUnits(siz.Height)) / 6;
            _circle = BodyFactory.CreateCircle(World, radius, (float)settings.DragStrengthSetting / 500);
            _circle.BodyType = BodyType.Dynamic;
            //_circle.Mass = 3;

            //_circle.LinearDamping = (float)settings.LinearDampingSetting;//2;
            _circle.LinearDamping = (float)settings.DampingRatioSetting;

            //_circle.Inertia
            //_circle.Restitution
            //_circle.Friction = 1;
            // _circle.Friction = 1;
            _circle.UserData = radius;
            _circle.Position = ConvertUnits.ToSimUnits(new Microsoft.Xna.Framework.Vector2((float)Location.X, (float)Location.Y));

            circle = Util.createellipsetexture(new Size(ConvertUnits.ToDisplayUnits(radius * 2), ConvertUnits.ToDisplayUnits(radius * 2)));
            if (App.DoNotCollide)
            {
                _circle.CollidesWith = Category.Cat10;
               
            }
            else 
            {
                _circle.CollidesWith = Category.All;
            }

            _circle.CollisionCategories = Category.Cat1;
            _circle.OnCollision += MyOnCollision;
            joint = new FixedDistanceJoint(_circle, _circle.LocalCenter, _circle.Position);

            //joint.Frequency = 3f;
            joint.Frequency = (float)settings.FrequencySetting;
            //joint.DampingRatio = 0.1f;
            joint.DampingRatio = (float)settings.DampingRatioSetting;
            joint.Length = 0f;
            World.AddJoint(joint);
            TextureCount = texturesList.Count;
            return texturesList.First();
        }

        public bool MyOnCollision(Fixture f1, Fixture f2, FarseerPhysics.Dynamics.Contacts.Contact contact)
        {

            //We want the collision to happen, so we return true.

            return true;

        }


        // private System.Threading.Timer hideRemoveButtonTimer;

        //private void hideRemoveButton(Object stateInfo)
        //{
        //    hideRemoveButtonTimer = null;
        //    Dispatcher.BeginInvoke(() =>
        //    {
        //        CloseBtnCirc.Visibility = System.Windows.Visibility.Collapsed;
        //        CloseBtnX.Visibility = System.Windows.Visibility.Collapsed;
        //    });

        //}

        public void Render(SpriteBatch spriteBatch)
        {
            try
            {
                Microsoft.Xna.Framework.Vector2 loc = ConvertUnits.ToDisplayUnits(this.joint.WorldAnchorA);
                Microsoft.Xna.Framework.Vector2 dest = ConvertUnits.ToDisplayUnits(this.joint.WorldAnchorB);
                double X = loc.X - dest.X;
                double Y = loc.Y - dest.Y;
                Size siz = getsize();
                foreach (Texture2D txt in texturesList)
                {
                    Microsoft.Xna.Framework.Vector2 pos = new Microsoft.Xna.Framework.Vector2((float)(loc.X - X * txt.Width * txt.Width / (siz.Width * siz.Width)), (float)(loc.Y - Y * txt.Height * txt.Height / (siz.Height * siz.Height)));
                    pos.X += -txt.Width / 2;// +ConvertUnits.ToDisplayUnits((float)_circle.UserData);
                    pos.Y += -txt.Height / 2;// +ConvertUnits.ToDisplayUnits((float)_circle.UserData);

                    txt.Tag = pos;
                }
                circle.Tag = Microsoft.Xna.Framework.Vector2.Add(loc, new Microsoft.Xna.Framework.Vector2(-circle.Width / 2, -circle.Height / 2));
            }
            catch
            {
                //TODO
            }

        }



        public void delete()
        {
            App.shakeObjects.Remove(this);
            parent.Children.Remove(this);
            DeleteCallback();
        }
        Action DeleteCallback;



        private void CloseBtnCirc_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {

            this.delete();
            e.Handled = true;
        }

        private void CloseBtnX_Tap(object sender, GestureEventArgs e)
        {

            this.delete();
            e.Handled = true;
        }



    }
}
